package top.ochiamalu.aioj.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import top.ochiamalu.aioj.common.ErrorCode;
import top.ochiamalu.aioj.domain.entity.Email;
import top.ochiamalu.aioj.enums.EmailEnum;
import top.ochiamalu.aioj.exception.BusinessException;
import top.ochiamalu.aioj.mapper.EmailMapper;
import top.ochiamalu.aioj.properties.AIOJProperties;
import top.ochiamalu.aioj.service.EmailService;
import top.ochiamalu.aioj.util.EmailUtils;

import javax.annotation.Resource;

/**
 * 电子邮件服务实现
 *
 * @author ochiamalu
 * @date 2025/03/31
 */
@Service
public class EmailServiceImpl extends ServiceImpl<EmailMapper, Email>
        implements EmailService {

    @Resource
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String fromEmail;

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "ochiamalu";

    @Resource
    private AIOJProperties aiojProperties;

    // 验证链接有效期：24小时
    private static final long VERIFICATION_EXPIRATION = 24 * 60 * 60 * 1000;

    @Override
    public void sendVerificationEmail(String toEmail, String verificationUrl) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(fromEmail);
        message.setTo(toEmail);
        message.setSubject("AI OJ - 验证您的邮箱");
        message.setText("请点击以下链接验证您的邮箱：\n\n" + verificationUrl + "\n\n" +
                "如果这不是您的操作，请忽略此邮件。\n\n" +
                "此链接将在24小时后失效。");

        try {
            mailSender.send(message);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "发送验证邮件失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modifyPrimaryEmail(Long emailId) {
        long userId = StpUtil.getLoginIdAsLong();

        Email email = this.getById(emailId);
        if (email == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱不存在");
        }
        if (email.getUserId() != userId) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限修改此邮箱");
        }

        boolean updated = lambdaUpdate().eq(Email::getUserId, userId)
                .set(Email::getIsPrimary, EmailEnum.FALSE.getCode())
                .update();
        if (!updated) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新用户邮箱失败");
        }
        return lambdaUpdate().eq(Email::getUserId, userId)
                .eq(Email::getId, emailId)
                .set(Email::getIsPrimary, EmailEnum.TRUE.getCode())
                .update();
    }

    @Override
    public Boolean resendVerificationEmail(Long emailId) {
        long userId = StpUtil.getLoginIdAsLong();
        Email email = this.getById(emailId);
        if (email == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱不存在");
        }
        if (email.getUserId() != userId) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限修改此邮箱");
        }

        // 生成验证token
        String verificationToken = EmailUtils.generateVerificationToken(userId, email.getEmail());

        boolean updated = this.lambdaUpdate().eq(Email::getId, emailId)
                .set(Email::getVerificationToken, verificationToken)
                .update();

        if (!updated) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }

        // 生成验证链接
        String verificationUrl = String.format(aiojProperties.getEmailVerificationUrl() + "/%s", verificationToken);

        // 发送验证邮件
        this.sendVerificationEmail(email.getEmail(), verificationUrl);
        return true;
    }

    @Override
    public Boolean deleteEmail(Long emailId) {
        long userId = StpUtil.getLoginIdAsLong();
        Email email = this.getById(emailId);
        if (email == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱不存在");
        }
        if (email.getUserId() != userId) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限删除此邮箱");
        }
        return this.removeById(emailId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean verifyEmail(String token) {
        // 检查token格式
        String[] parts = token.split(":");
        if (parts.length != 3) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证链接无效");
        }

        String encryptedTimestamp = parts[0];
        long timestamp;
        try {
            timestamp = Long.parseLong(parts[1]);
        } catch (NumberFormatException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证链接无效");
        }

        // 验证时间戳是否被篡改
        String expectedEncryptedTimestamp = DigestUtils.md5DigestAsHex((timestamp + SALT).getBytes());
        if (!expectedEncryptedTimestamp.equals(encryptedTimestamp)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证链接无效");
        }

        // 检查是否过期
        if (System.currentTimeMillis() - timestamp > VERIFICATION_EXPIRATION) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证链接已过期");
        }

        // 根据完整token查找邮箱记录
        Email email = lambdaQuery()
                .eq(Email::getVerificationToken, token)
                .one();

        if (email == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "验证链接无效");
        }

        // 检查是否已验证
        if (EmailEnum.TRUE.getCode().equals(email.getIsVerified())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "邮箱已验证");
        }

        // 更新验证状态
        email.setIsVerified(EmailEnum.TRUE.getCode());

        // 如果用户没有主邮箱，将此邮箱设为主邮箱
        if (lambdaQuery()
                .eq(Email::getUserId, email.getUserId())
                .eq(Email::getIsPrimary, EmailEnum.TRUE.getCode())
                .count() == 0) {
            email.setIsPrimary(EmailEnum.TRUE.getCode());
        }

        // 清除验证token
        email.setVerificationToken(null);

        return this.updateById(email);
    }
}
